using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using StackExchange.Redis;

namespace Catastrophe.Common.Redis
{
	public class RedisUtil
	{
		private ConnectionMultiplexer _redis;

		private IDatabase _database;

		public Action<string, string> RedisSubMessageEvent;

		public int TimeOut { get; set; }

		public bool IsConnected => this._redis?.IsConnected ?? false;

		public void Connect(string url = "127.0.0.1:6379")
		{
			try
			{
				this._redis = ConnectionMultiplexer.Connect(url);
				this._database = this._redis.GetDatabase();
			}
			catch (Exception innerException)
			{
				throw new Exception("连接Redis失败！", innerException);
			}
		}

		public bool StringSet(string key, dynamic value, TimeSpan? expiry = null)
		{
			return this._database.StringSet(key, value, expiry);
		}

		public T StringGet<T>(string key)
		{
			RedisValue value = this._database.StringGet(key);
			return this.ConvertObj<T>(value);
		}

		public string StringGet(string key)
		{
			return this._database.StringGet(key).ToString();
		}

		public dynamic StringGet(string key, int start, int end)
		{
			return this._database.StringGetRange(key, start, end).ToString();
		}

		public dynamic StringGetSet(string key, string value)
		{
			return this._database.StringGetSet(key, value);
		}

		public bool StringGetBit(string key, long offset)
		{
			return this._database.StringGetBit(key, offset);
		}

		public List<dynamic> StringMultiGet(string[] keys)
		{
			List<object> list = new List<object>();
			foreach (string text in keys)
			{
				list.Add(this._database.StringGet(text));
			}
			return list;
		}

		public bool StringSetBit(string key, long offset, bool flag = true)
		{
			return this._database.StringSetBit(key, offset, flag);
		}

		public bool StringSet(string key, string value, TimeSpan expiry)
		{
			return this._database.StringSet(key, value, expiry);
		}

		public void StringSetIfAbsent(string key, string value)
		{
			if (this._database.StringGet(key) == RedisValue.Null)
			{
				this._database.StringSet(key, value);
			}
		}

		public dynamic StringSet(string key, long offset, string value)
		{
			return this._database.StringSetRange(key, offset, value);
		}

		public long StringSize(string key)
		{
			return this._database.StringLength(key);
		}

		public void StringMultiSet(Dictionary<string, string> dic)
		{
			foreach (KeyValuePair<string, string> item in dic)
			{
				this._database.StringSet(item.Key, item.Value);
			}
		}

		public void StringMultiSetIfAbsent(Dictionary<string, string> dic)
		{
			foreach (KeyValuePair<string, string> item in dic)
			{
				if (this._database.StringGet(item.Key) == RedisValue.Null)
				{
					this._database.StringSet(item.Key, item.Value);
				}
			}
		}

		public double StringIncrement(string key, double value)
		{
			return this._database.StringIncrement(key, value);
		}

		public long StringAppend(string key, string value)
		{
			return this._database.StringAppend(key, value);
		}

		public bool StringDelete(string key)
		{
			return this._database.KeyDelete(key);
		}

		public bool HashDelete(string key, string field)
		{
			return this._database.HashDelete(key, field);
		}

		public bool HashHasKey(string key, string field)
		{
			return this._database.HashExists(key, field);
		}

		public T HashGet<T>(string key, dynamic field)
		{
			string text = this._database.HashGet(key, field);
			return this.ConvertObj<T>(text);
		}

		public string HashGet(string key, dynamic field)
		{
			return this._database.HashGet(key, field);
		}

		public List<T> HashGetValueList<T>(string key)
		{
			RedisValue[] values = this._database.HashValues(key);
			return this.ConvetList<T>(values);
		}

		public List<T> HashGetKeyList<T>(string key)
		{
			RedisValue[] values = this._database.HashKeys(key);
			return this.ConvetList<T>(values);
		}

		public Dictionary<dynamic, object> HashGetAll(string key)
		{
			Dictionary<object, object> dictionary = new Dictionary<object, object>();
			HashEntry[] array = this._database.HashGetAll(key);
			HashEntry[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				HashEntry hashEntry = array2[i];
				dictionary.Add(hashEntry.Name, hashEntry.Value);
			}
			return dictionary;
		}

		public double HashIncrement(string key, string field, double value)
		{
			return this._database.HashIncrement(key, field, value);
		}

		public string[] HashKeys(string key)
		{
			return this._database.HashKeys(key).ToStringArray();
		}

		public long HashSize(string key)
		{
			return this._database.HashLength(key);
		}

		public List<dynamic> HashMultiGet(string key, List<string> hashKeys)
		{
			List<object> list = new List<object>();
			foreach (string hashKey in hashKeys)
			{
				list.Add(this._database.HashGet(key, hashKey));
			}
			return list;
		}

		public void HashPutAll(string key, Dictionary<string, dynamic> dic)
		{
			List<HashEntry> list = new List<HashEntry>();
			for (int i = 0; i < dic.Count; i++)
			{
				KeyValuePair<string, object> keyValuePair = dic.ElementAt(i);
				list.Add(new HashEntry(keyValuePair.Key, (dynamic)(keyValuePair.Value ?? "")));
			}
			this._database.HashSet(key, list.ToArray());
		}

		public bool HashPut<T>(string key, dynamic field, T t)
		{
			string text = this.ConvertJson(t);
			return this._database.HashSet(key, field, text);
		}

		public bool HashPut(string key, dynamic field, string value)
		{
			return this._database.HashSet(key, field, value);
		}

		public void HashPutIfAbsent(string key, string field, string value)
		{
			if (!this.HashHasKey(key, field))
			{
				this._database.HashSet(key, new HashEntry[1]
				{
					new HashEntry(field, value ?? "")
				});
			}
		}

		public string[] HashValues(string key)
		{
			return this._database.HashValues(key).ToStringArray();
		}

		public T GetHashValue<T>(string key)
		{
			HashEntry[] array = this._database.HashGetAll(key);
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			HashEntry[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				HashEntry hashEntry = array2[i];
				dictionary.Add((string?)hashEntry.Name, hashEntry.Value);
			}
			string json = JsonSerializer.Serialize(dictionary);
			return JsonSerializer.Deserialize<T>(json);
		}

		public void SetHashValue<T>(T t, string key)
		{
			string json = JsonSerializer.Serialize(t);
			Dictionary<string, object> dic = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
			this.HashPutAll(key, dic);
		}

		public long ListLeftPush(string key, string value)
		{
			return this._database.ListLeftPush(key, value);
		}

		public dynamic ListLeftPop(string key)
		{
			return this._database.ListLeftPop(key);
		}

		public long ListSize(string key)
		{
			return this._database.ListLength(key);
		}

		public string[] ListRange(string key, int start, int end)
		{
			return this._database.ListRange(key, start, end).ToStringArray();
		}

		public long ListRemove(string key, string value)
		{
			return this._database.ListRemove(key, value, 0L);
		}

		public dynamic ListIndex(string key, long index)
		{
			return this._database.ListGetByIndex(key, index);
		}

		public void ListSet(string key, int index, dynamic value)
		{
			this._database.ListSetByIndex(key, index, value);
		}

		public string ListGet(string key, int index)
		{
			return this._database.ListGetByIndex(key, index).ToString();
		}

		public void ListTrim(string key, int start, int end)
		{
			this._database.ListTrim(key, start, end);
		}

		public dynamic ListRightPopAndLeftPush(string sourceKey, string destinationKey)
		{
			return this._database.ListRightPopLeftPush(sourceKey, destinationKey);
		}

		public void SetAdd(string key, string value)
		{
			this._database.SetAdd(key, value);
		}

		public string[] SetCombine(int point, string firstKey, string secondKey)
		{
			return (point switch
			{
				0 => this._database.SetCombine(SetOperation.Union, firstKey, secondKey), 
				1 => this._database.SetCombine(SetOperation.Intersect, firstKey, secondKey), 
				2 => this._database.SetCombine(SetOperation.Difference, firstKey, secondKey), 
				_ => new RedisValue[0], 
			}).ToStringArray();
		}

		public bool SetContains(string key, string value)
		{
			return this._database.SetContains(key, value);
		}

		public long SetLength(string key)
		{
			return this._database.SetLength(key);
		}

		public string[] SetMembers(string key)
		{
			return this._database.SetMembers(key).ToStringArray();
		}

		public bool SetMove(string sourceKey, string destinationKey, string value)
		{
			return this._database.SetMove(sourceKey, destinationKey, value);
		}

		public string SetPop(string key)
		{
			return (string?)this._database.SetPop(key);
		}

		public string SetRandomMember(string key)
		{
			return (string?)this._database.SetRandomMember(key);
		}

		public string[] SetRandomMembers(string key, long count)
		{
			return this._database.SetRandomMembers(key, count).ToStringArray();
		}

		public void SetRemove(string key, string value)
		{
			this._database.SetRemove(key, value);
		}

		public void SetScan(string key)
		{
			this._database.SetScan(key, default(RedisValue), 250, 0L);
		}

		public void Method(string key, string value, double score)
		{
			this._database.SortedSetAdd(key, new SortedSetEntry[1]
			{
				new SortedSetEntry(value, score)
			});
		}

		public RedisResult Excute(string command, object[] objParams)
		{
			return this._database.Execute(command, objParams);
		}

		public void Expire(string key, TimeSpan? ts)
		{
			this._database.KeyExpire(key, ts);
		}

		public void Expire(string key, DateTime? ts)
		{
			this._database.KeyExpire(key, ts);
		}

		public void ExpireAsync(string key, TimeSpan? ts)
		{
			this._database.KeyExpireAsync(key, ts);
		}

		public void ExpireAsync(string key, DateTime? ts)
		{
			this._database.KeyExpireAsync(key, ts);
		}

		public void Save()
		{
			this.Excute("SAVE", null);
		}

		public void BgSave()
		{
			this.Excute("BGSAVE", null);
		}

		private string ConvertJson<T>(T value)
		{
			return (value is string) ? value.ToString() : JsonSerializer.Serialize(value);
		}

		private T ConvertObj<T>(RedisValue value)
		{
			return JsonSerializer.Deserialize<T>((string?)value);
		}

		private List<T> ConvetList<T>(RedisValue[] values)
		{
			List<T> list = new List<T>();
			foreach (RedisValue value in values)
			{
				T item = this.ConvertObj<T>(value);
				list.Add(item);
			}
			return list;
		}

		private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
		{
			return ((IEnumerable<string>)redisKeys).Select((Func<string, RedisKey>)((string redisKey) => redisKey)).ToArray();
		}

		public void RedisSub(string subChannel)
		{
			this._redis?.GetSubscriber().Subscribe(subChannel, delegate(RedisChannel channel, RedisValue message)
			{
				this.RedisSubMessageEvent?.Invoke((string?)channel, (string?)message);
			});
		}

		public long RedisPub<T>(string channel, T msg)
		{
			if (this._redis != null)
			{
				return this._redis.GetSubscriber().Publish(channel, JsonSerializer.Serialize(msg));
			}
			return -1L;
		}

		public void Unsubscribe(string channel)
		{
			this._redis.GetSubscriber().Unsubscribe(channel);
		}

		public void UnsubscribeAll()
		{
			this._redis.GetSubscriber().UnsubscribeAll();
		}
	}
}
